#pragma once

#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>     // 包含可变参数列表的宏和类型的头文件
#include <sys/types.h> //包含系统调用数据类型的头文件
#include <unistd.h>    //包含系统调用函数的头文件
#include <pthread.h>
#include "LockGuard.hpp"

bool gIsSave = false;                  // 是否保存日志文件
const std::string logname = "log.txt"; // 日志文件

// 日志等级
enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

void SaveFile(const std::string &filename, const std::string &message)
{
    std::ofstream out(filename, std::ios::app); // 以追加的方式输出到log.txt文件
    if (!out.is_open())
    {
        return;
    }
    out << message;
    out.close();
}

std::string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "Debug";
    case INFO:
        return "Info";
    case WARNING:
        return "Warning";
    case ERROR:
        return "Error";
    case FATAL:
        return "Fatal";
    default:
        return "Unkown";
    }
}

std::string GetTimeString()
{
    time_t curr_time = time(nullptr);               // 获得时间戳
    struct tm *format_time = localtime(&curr_time); // 将时间戳转化为本地时间
    if (format_time == nullptr)
    {
        return "None";
    }
    char time_buffer[1024];
    snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec); //%d为格式说明符，填充指定类型参数
    return time_buffer;
}

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

// 日志格式：日志等级 时间 代码所在的文件名/行数 日志内容
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{ // 根据格式化字符串的格式说明符来确认可变参数类型
    std::string levelstr = LevelToString(level);
    std::string timestr = GetTimeString();
    pid_t selfid = getpid();

    char buffer[1024];
    va_list arg;                                    // 在函数内部存储可变参数列表的信息
    va_start(arg, format);                          // 初始化arg，从format参数之后的第一个参数开始访问可变参数列表
    vsnprintf(buffer, sizeof(buffer), format, arg); // 将可变参数列表按照format指定的格式格式化为一个字符串
    va_end(arg);                                    // 清理arg变量

    std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +
                          "[" + std::to_string(selfid) + "]" +
                          "[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer + "\n";
    LockGuard lockguard(&lock); // 加锁保护对log.txt和显示器的访问

    if (!issave)
    {
        std::cout << message;
    }
    else
    {
        SaveFile(logname, message);
    }
}

// 使用LOG宏只用传递level，format参数，宏替换时__FILE__，__LINE__被自动填充，并替换为文件名和行号
#define LOG(level, format, ...) do{LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__);} while (0)

#define EnableFile()do{gIsSave = true;}while (0)

#define EnableScreen()do{gIsSave = false;} while (0)
